﻿/**
 * Copyright(c) Live2D Inc. All rights reserved.
 *
 * Use of this source code is governed by the Live2D Open Software license
 * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
 */
#include "../include/Live2dDelegate.hpp"
#include "../include/Live2dPal.hpp"
#include "../include/Live2dView.hpp"
#include "../include/Live2dTextureManager.hpp"
#include "../include/Live2dModelManager.hpp"

#include "../../libcat/include/cat-window.hpp"

#include <iostream>
#include <GL/glew.h>
#include <GLFW/glfw3.h>


using namespace Live2dDefine;
using namespace std;


namespace {
    Live2dDelegate* s_instance = NULL;
}

Live2dDelegate* Live2dDelegate::GetInstance()
{
    if (s_instance == NULL)
    {
        s_instance = new Live2dDelegate();
    }

    return s_instance;
}

void Live2dDelegate::ReleaseInstance()
{
    if (s_instance != NULL)
    {
        delete s_instance;
    }

    s_instance = NULL;
}

bool Live2dDelegate::Initialize()
{
    if(DebugLogEnable)
        Live2dPal::PrintLog("START");


    if (glfwInit() == GL_FALSE)
    {
        if (DebugLogEnable)
            Live2dPal::PrintLog("Can't initilize GLFW");

        return GL_FALSE;
    }

    glfwWindowHint(GLFW_VISIBLE, GL_FALSE);
    _window = glfwCreateWindow(RenderTargetWidth, RenderTargetHeight, "Live2D", NULL, NULL);
    if (_window == NULL)
    {
        if (DebugLogEnable)
            Live2dPal::PrintLog("Can't initilize GLFW");

        glfwTerminate();
        return GL_FALSE;
    }

    // Windowのコンテキストをカレントに設定
    glfwMakeContextCurrent(_window);

    if (glewInit() != GLEW_OK) {
        if (DebugLogEnable)
            Live2dPal::PrintLog("Can't initilize GLFW");

        glfwTerminate();
        return GL_FALSE;
    }
    // Cubism SDK の初期化
    InitializeCubism();

    isInit = true;

    return GL_TRUE;
}

bool Live2dDelegate::IsInitialize()
{
    return isInit;
}

void Live2dDelegate::Release()
{
    if (DebugLogEnable)
        Live2dPal::PrintLog("STOP");

    glfwDestroyWindow(_window);

    glfwTerminate();

    delete _textureManager;
    delete _view;

    //Cubism SDK の解放
    CubismFramework::Dispose();

    isInit = false;
}

void Live2dDelegate::Run(void* texture)
{
    Live2dPal::UpdateTime();
    //sf::RenderTexture* offscreen = (sf::RenderTexture*)texture; 
    //_view->SetRenderBufferTarget(offscreen->getTexture().getNativeHandle(), offscreen->getSize().x, offscreen->getSize().y);
    _view->Render();
}

bool Live2dDelegate::Resize(int width, int height)
{
    if (width < 100 || height < 100)
        return false;

    if (_window == NULL)
        return false;

    glfwSetWindowSize(_window, width, height);
    //AppViewの初期化
    _view->Initialize();
    // ビューポート変更
    glViewport(0, 0, width, height);

    return true;
}

bool Live2dDelegate::Scale(float s_x, float s_y)
{
    if (_view == NULL)
        return false;

    return _view->ScaleView(s_x, s_y);
}

bool Live2dDelegate::Translate(float t_x, float t_y)
{
    if (_view == NULL)
        return false;

    return _view->TranslateView(t_x,t_y);
}

bool Live2dDelegate::LoadModelFromFile(const char* dir, const char* fileName)
{  
    return  Live2dModelManager::GetInstance()->ChangeScene(dir,fileName);
}

void Live2dDelegate::ReleaseModel()
{
    Live2dModelManager::GetInstance()->ReleaseScene();
}

void Live2dDelegate::OnMouseClickCallBack(const cat_mouse_click_event* event, bool mouse_up)
{
    if (_view == NULL)
        return;
    _view->OnMouseClick(event->_button, mouse_up);
}

void Live2dDelegate::OnMouseMoveCallBack(float m_x, float m_y)
{
    if (_view == NULL)
        return;
    _view->OnMouseMoved(m_x, m_y);
}

void Live2dDelegate::OnLeftHandDownCallBack(bool key_up)
{
    if (_view == NULL)
        return;
    _view->OnLeftHandDown(key_up);
}

void Live2dDelegate::OnRightHandDownCallBack(bool key_up)
{
    if (_view == NULL)
        return;
    _view->OnRightHandDown(key_up);
}

void Live2dDelegate::OnStickLeftDownCallBack(bool key_up)
{
    if (_view == NULL)
        return;
    _view->OnStickLeftDown(key_up);
}

void Live2dDelegate::OnStickRightDownCallBack(bool key_up)
{
    if (_view == NULL)
        return;
    _view->OnStickRightDown(key_up);
}

void Live2dDelegate::OnStickShowLeftHandCallBack(bool key_up)
{
    if (_view == NULL)
        return;
    _view->OnStickShowLeftHand(key_up);
}

void Live2dDelegate::OnStickShowRightHandCallBack(bool key_up)
{
    if (_view == NULL)
        return;
    _view->OnStickShowRightHand(key_up);
}

void Live2dDelegate::OnStickLCallBack(float x, float y)
{
    if (_view == NULL)
        return;
    _view->OnStickL(x,y);
}

void Live2dDelegate::OnStickRCallBack(float x, float y)
{
    if (_view == NULL)
        return;
    _view->OnStickR(x,y);
}

void Live2dDelegate::StartMotion(const char* group, const int id)
{
    if (_view == NULL)
        return;
    _view->StartMotion(group, id);
}

void Live2dDelegate::StartExpression(const char * expressionId)
{
    if (_view == NULL)
        return;
    _view->SetExpression(expressionId);

}

void Live2dDelegate::StartExpressionById(const int id)
{
    if (_view == NULL)
        return;
    _view->SetExpressionById(id);
}

void Live2dDelegate::SetSpeed(const float speed)
{
    if (_view == NULL)
        return;
    _view->SetSpeed(speed);
}

void Live2dDelegate::SetRandomMotion(const bool random , const bool randomExpression)
{
    if (_view == NULL)
        return;
    _view->SetRandomMotion(random , randomExpression);
}

GLuint Live2dDelegate::CreateShader()
{//バーテックスシェーダのコンパイル
    GLuint vertexShaderId = glCreateShader(GL_VERTEX_SHADER);
    const char* vertexShader =
        "#version 120\n"
        "attribute vec3 position;"
        "attribute vec2 uv;"
        "varying vec2 vuv;"
        "void main(void){"
        "    gl_Position = vec4(position, 1.0);"
        "    vuv = uv;"
        "}";
    glShaderSource(vertexShaderId, 1, &vertexShader, NULL);
    glCompileShader(vertexShaderId);
    if (!CheckShader(vertexShaderId))
    {
        return 0;
    }

    //フラグメントシェーダのコンパイル
    GLuint fragmentShaderId = glCreateShader(GL_FRAGMENT_SHADER);
    const char* fragmentShader =
        "#version 120\n"
        "varying vec2 vuv;"
        "uniform sampler2D texture;"
        "uniform vec4 baseColor;"
        "void main(void){"
        "    gl_FragColor = texture2D(texture, vuv) * baseColor;"
        "}";
    glShaderSource(fragmentShaderId, 1, &fragmentShader, NULL);
    glCompileShader(fragmentShaderId);
    if (!CheckShader(fragmentShaderId))
    {
        return 0;
    }

    //プログラムオブジェクトの作成
    GLuint programId = glCreateProgram();
    glAttachShader(programId, vertexShaderId);
    glAttachShader(programId, fragmentShaderId);

    // リンク
    glLinkProgram(programId);

    glUseProgram(programId);

    return programId;
}

Live2dDelegate::Live2dDelegate():
    isInit(false),
    _cubismOption(),
    _window(NULL)
{
    _view = new Live2dView();
    _textureManager = new Live2dTextureManager();
}

Live2dDelegate::~Live2dDelegate()
{
   
}

void Live2dDelegate::InitializeCubism()
{
    _cubismOption.LogFunction = Live2dPal::PrintMessage;
    _cubismOption.LoggingLevel = Live2dDefine::CubismLoggingLevel;
    Csm::CubismFramework::StartUp(&_cubismAllocator, &_cubismOption);

    //Initialize cubism
    CubismFramework::Initialize();

    Live2dPal::UpdateTime();
}

bool Live2dDelegate::CheckShader(GLuint shaderId)
{
    GLint status;
    GLint logLength;
    glGetShaderiv(shaderId, GL_INFO_LOG_LENGTH, &logLength);
    if (logLength > 0)
    {
        GLchar* log = reinterpret_cast<GLchar*>(CSM_MALLOC(logLength));
        glGetShaderInfoLog(shaderId, logLength, &logLength, log);
        CubismLogError("Shader compile log: %s", log);
        CSM_FREE(log);
    }

    glGetShaderiv(shaderId, GL_COMPILE_STATUS, &status);
    if (status == GL_FALSE)
    {
        glDeleteShader(shaderId);
        return false;
    }

    return true;
}


